/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.service.impl;

import com.bifrost.constant.OrderStatus;
import com.bifrost.constant.OrderType;
import com.bifrost.entity.Order;
import com.bifrost.exception.CheckoutException;
import com.bifrost.exception.PricingException;
import com.bifrost.exception.RequiredAttributeNotProvidedException;
import com.bifrost.exception.WorkflowException;
import com.bifrost.model.CheckoutResponse;
import com.bifrost.service.CheckoutService;
import com.bifrost.service.OrderService;
import com.bifrost.util.NumberUtils;
import com.bifrost.workflow.ProcessContext;
import com.bifrost.workflow.Processor;
import com.bifrost.workflow.checkout.CheckoutSeed;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author joewee
 * @version 1.0.0
 * @Date 2017/11/16 14:11
 */
@Service("checkoutService")
public class CheckoutServiceImpl implements CheckoutService {
    @Resource
    protected Processor<CheckoutSeed, CheckoutSeed> checkoutWorkflow;
    @Autowired
    OrderService orderService;
    /**
     * 利用map锁确保在只有一个请求能处理该订单
     */
    protected static ConcurrentMap<Long, Object> lockMap = new ConcurrentHashMap<>();

    /**
     * 结算订单service
     *
     * @param order 订单
     * @return 结算后的订单
     * @throws CheckoutException 异常
     */
    @Override
    @Transactional(rollbackFor = {CheckoutException.class})
    public CheckoutResponse performCheckout(Order order) throws CheckoutException {
        try {
            //非优惠券订单
            if(!OrderType.CASH_BY_COUPON.equalsIgnoreCase(order.getOrderType())){
                orderService.save(order, true);
            }
        } catch (PricingException e) {
            throw new CheckoutException("该订单保存失败-id: " + order.getOrderId(), new CheckoutSeed(order, new HashMap<String, Object>()));
        }
        Object lockObject = putLock(order.getOrderId());
        if (lockObject != null) {
            throw new CheckoutException("该订单已经被处理-id: " + order.getOrderId(), new CheckoutSeed(order, new HashMap<String, Object>()));
        }
        // Immediately fail if this order has already been checked out previously
        if (hasOrderBeenCompleted(order)) {
            throw new CheckoutException("This order has already been submitted or cancelled, unable to checkout order -- id: " + order.getOrderId(), new CheckoutSeed(order, new HashMap<String, Object>()));
        }
        CheckoutSeed seed = null;
        try {

            order = orderService.save(order, false);
            order.setOrderNumber(NumberUtils.determineOrderNumber(order));
            seed = new CheckoutSeed(order, new HashMap<String, Object>());
            ProcessContext<CheckoutSeed> context = checkoutWorkflow.doActivities(seed);
            // We need to pull the order off the seed and save it here in case any activity modified the order.
            order = orderService.save(seed.getOrder(), false);
            //order.getOrderMessages().addAll(((ActivityMessages) context).getActivityMessages());
            seed.setOrder(order);
            return seed;
        } catch (PricingException e) {
            throw new CheckoutException("Unable to checkout order -- id: " + order.getOrderId(), e, seed);
        } catch (WorkflowException e) {
            throw new CheckoutException("Unable to checkout order -- id: " + order.getOrderId(), e.getRootCause(), seed);
        } catch (RequiredAttributeNotProvidedException e) {
            throw new CheckoutException("Unable to checkout order -- id: " + order.getOrderId(), e.getCause(), seed);
        } finally {
            // The order has completed processing, remove the order from the processing map
            removeLock(order.getOrderId());
        }
    }

    private boolean hasOrderBeenCompleted(Order order) {
        return OrderStatus.PURCHASED.equals(order.getOrderStatus()) || OrderStatus.CANCELED.equals(order.getOrderStatus());
    }

    /**
     * 根据订单 id 获取 对象锁
     *
     * @param orderId
     * @return null-如果不存在可用对象锁<br>
     * 如果返回了对象锁，意味着已经有一个线程在处理
     */
    protected Object putLock(Long orderId) {
        return lockMap.putIfAbsent(orderId, new Object());
    }

    /**
     * 移除指定id的对象锁
     *
     * @param orderId
     */
    protected void removeLock(Long orderId) {
        lockMap.remove(orderId);
    }

}
